package fun.qu_an.minecraft.velocity.mgh.config;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import fun.qu_an.minecraft.velocity.mgh.BuildConstants;
import fun.qu_an.minecraft.velocity.mgh.MiniGamesHelper;
import fun.qu_an.minecraft.velocity.mgh.encoding.StringEncoding;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import lombok.SneakyThrows;

import java.io.FileReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;

public class MGHConfig implements Serializable {
	public static final String END_OF_OPTION = "mgh:end_of_option";
	private static final Gson GSON = new GsonBuilder().setVersion(1.0).disableHtmlEscaping().setPrettyPrinting().setLenient().create();
	public static final Path DATA_DIRECTORY = Path.of("plugins").resolve(BuildConstants.PLUGIN_NAME);
	private static final Map<String, MGHConfig> groups = new HashMap<>();
	private static final Map<String, byte[]> serialized = new HashMap<>();
	private static final String defaultJson = GSON.toJson(new MGHConfig());

	@SneakyThrows
	public static synchronized void reload() {
		groups.clear();
		serialized.clear();
		for (RegisteredServer server : MiniGamesHelper.getInstance().getProxyServer().getAllServers()) {
			String name = server.getServerInfo().getName();
			Path serverCfgPath = DATA_DIRECTORY.resolve(name + ".json");

			if (!Files.exists(serverCfgPath)) {
				Files.createDirectories(serverCfgPath.getParent());
				Files.createFile(serverCfgPath);
				Files.writeString(serverCfgPath, defaultJson);
				groups.put(name, new MGHConfig());
				continue;
			}

			MGHConfig config;
			try {
				config = GSON.fromJson(new FileReader(serverCfgPath.toFile()), MGHConfig.class);
			} catch (Exception e) {
				Files.writeString(serverCfgPath, defaultJson);
				groups.put(name, new MGHConfig());
				continue;
			}
			groups.put(name, config);
		}
	}

	public static byte[] getSerialized(String serverId) {
		return serialized.computeIfAbsent(serverId, k -> {
			ByteBuf buf = Unpooled.buffer();
			groups.get(k).serialize(buf);
			byte[] bytes = new byte[buf.readableBytes()];
			buf.readBytes(bytes);
			return bytes;
		});
	}

	Group survival = new Group();
	Group creative = new Group();
	Group adventure = new Group();
	Group spectator = new Group();

	@Override
	public void serialize(ByteBuf buf) {
		survival.serialize(buf);
		creative.serialize(buf);
		adventure.serialize(buf);
		spectator.serialize(buf);
	}

	public static class Group implements Serializable {
		boolean disableHitBox = false;
		boolean hidePlayerListGameMode = false;
		boolean disableRecipeBook = false;
		ForceShader forceShader = new ForceShader();
		ForceColorSpace forceColorSpace = new ForceColorSpace();
		Option[] options = new Option[0];

		@Override
		public void serialize(ByteBuf buf) {
			buf.writeBoolean(disableHitBox);
			buf.writeBoolean(hidePlayerListGameMode);
			buf.writeBoolean(disableRecipeBook);
			forceShader.serialize(buf);
			forceColorSpace.serialize(buf);
			for (Option option : options) {
				option.serialize(buf);
			}
			StringEncoding.encode(buf, END_OF_OPTION);
		}
	}

	public static class ForceShader implements Serializable {
		boolean enabled = false;
		boolean tryServerResourcePack = true;
		String shaderPrompt = "";
		String shaderHash = "";
		String shaderUrl = "";

		@Override
		public void serialize(ByteBuf buf) {
			buf.writeBoolean(enabled);
			buf.writeBoolean(tryServerResourcePack);
			StringEncoding.encode(buf, shaderPrompt);
			StringEncoding.encode(buf, shaderHash);
			StringEncoding.encode(buf, shaderUrl);
		}
	}

	public static class ForceColorSpace implements Serializable {
		boolean enabled = false;
		MGHColorSpace colorSpace = MGHColorSpace.SRGB;

		@Override
		public void serialize(ByteBuf buf) {
			buf.writeBoolean(enabled);
			buf.writeInt(colorSpace.ordinal());
		}
	}

	public static class Option implements Serializable {
		String key = "";
		String value = "";

		@Override
		public void serialize(ByteBuf buf) {
			StringEncoding.encode(buf, key);
			StringEncoding.encode(buf, value);
		}
	}

}
